package server.items;

import constants.AboutDbg;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import constants.GameConstants;
import client.DbgAccount;
import client.DbgPlayer;
import client.items.IItem;
import client.items.ItemFactory;
import client.items.DbgInventoryType;
import database.MYSQL;
import database.MYSQLException;
import packet.creators.MainPacketCreator;

public class DbgStorage {

    private int id;
    private List<IItem> items;
    private int meso;
    private byte slots;
    private boolean changed = false;
    private Map<DbgInventoryType, List<IItem>> typeItems = new HashMap<DbgInventoryType, List<IItem>>();

    private int accid;
    
    
    private DbgStorage(int id, byte slots, int meso, int accid) {
	this.id = id;
	this.slots = slots;
	this.items = new LinkedList<IItem>();
	this.meso = meso;
        this.accid = accid;
    }
    
    public int getAccId() {
        return accid;
    }
    
    
    public static int create(int id) throws SQLException {
	Connection con = MYSQL.getConnection();
	PreparedStatement ps = con.prepareStatement("INSERT INTO storages (accountid, slots, meso) VALUES (?, ?, ?)", MYSQL.RETURN_GENERATED_KEYS);
	ps.setInt(1, id);
	ps.setInt(2, 4);
	ps.setInt(3, 0);
	ps.executeUpdate();

	int storageid;
	ResultSet rs = ps.getGeneratedKeys();
	if (rs.next()) {
	    storageid = rs.getInt(1);
	    ps.close();
	    rs.close();
	    return storageid;
	}
	ps.close();
	rs.close();
	throw new MYSQLException("Inserting char failed.");
    }

    public static DbgStorage loadStorage(int id) {
	DbgStorage ret = null;
	int storeId;
	try {
	    Connection con = MYSQL.getConnection();
	    PreparedStatement ps = con.prepareStatement("SELECT * FROM storages WHERE accountid = ?");
	    ps.setInt(1, id);
	    ResultSet rs = ps.executeQuery();

	    if (rs.next()) {
		storeId = rs.getInt("storageid");
		ret = new DbgStorage(storeId, rs.getByte("slots"), rs.getInt("meso"), id);
		rs.close();
		ps.close();
                ItemFactory.loadItemsFromStorage(ret);
	    } else {
		storeId = create(id);
		ret = new DbgStorage(storeId, (byte) 4, 0, id);
	    }
	} catch (SQLException ex) {
	    System.err.println("Error loading storage" + ex);
	}
	return ret;
    }

    public void saveToDB(DbgPlayer hp) {
	if (!changed) {
	    return;
	}
	try {
	    Connection con = MYSQL.getConnection();

	    PreparedStatement ps = con.prepareStatement("UPDATE storages SET slots = ?, meso = ? WHERE storageid = ?");
	    ps.setInt(1, slots);
	    ps.setInt(2, meso);
	    ps.setInt(3, id);
	    ps.executeUpdate();
	    ps.close();

	    ps = con.prepareStatement("DELETE FROM inventoryitems WHERE accountid = ? AND type = ?");
	    ps.setInt(1, accid);
            ps.setInt(2, ItemFactory.getType(ItemFactory.InventoryType.STORAGE));
	    ps.executeUpdate();
	    ps.close();

            ItemFactory.saveItemsFromStorage(hp,this);
            
	} catch (Exception ex) {
	    System.err.println("Error saving storage");
            if (!AboutDbg.realese) ex.printStackTrace();
	}
    }

    public IItem takeOut(byte slot) {
	changed = true;
	IItem ret = items.remove(slot);
	DbgInventoryType type = GameConstants.getInventoryType(ret.getItemId());
	typeItems.put(type, new ArrayList<IItem>(filterItems(type)));
	return ret;
    }

    public void store(IItem item) {
	changed = true;
	items.add(item);
	DbgInventoryType type = GameConstants.getInventoryType(item.getItemId());
	typeItems.put(type, new ArrayList<IItem>(filterItems(type)));
    }

    public List<IItem> getItems() {
	return items;
    }

    private List<IItem> filterItems(DbgInventoryType type) {
	List<IItem> ret = new LinkedList<IItem>();

	for (IItem item : items) {
	    if (GameConstants.getInventoryType(item.getItemId()) == type) {
		ret.add(item);
	    }
	}
	return ret;
    }

    public byte getSlot(DbgInventoryType type, byte slot) {
	// MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
	byte ret = 0;
	for (IItem item : items) {
	    if (item == typeItems.get(type).get(slot)) {
		return ret;
	    }
	    ret++;
	}
	return -1;
    }

    public void sendStorage(DbgAccount c, int npcId) {
	// sort by inventorytype to avoid confusion
	Collections.sort(items, new Comparator<IItem>() {
            @Override
	    public int compare(IItem o1, IItem o2) {
		if (GameConstants.getInventoryType(o1.getItemId()).getType() < GameConstants.getInventoryType(o2.getItemId()).getType()) {
		    return -1;
		} else if (GameConstants.getInventoryType(o1.getItemId()) == GameConstants.getInventoryType(o2.getItemId())) {
		    return 0;
		} else {
		    return 1;
		}
	    }
	});
	for (DbgInventoryType type : DbgInventoryType.values()) {
	    typeItems.put(type, new ArrayList<IItem>(items));
	}
	c.getSession().write(MainPacketCreator.getStorage(npcId, slots, items, meso));
    }

    public void sendStored(DbgAccount c, DbgInventoryType type) {
	c.getSession().write(MainPacketCreator.storeStorage(slots, type, typeItems.get(type)));
    }

    public void sendTakenOut(DbgAccount c, DbgInventoryType type) {
	c.getSession().write(MainPacketCreator.takeOutStorage(slots, type, typeItems.get(type)));
    }

    public int getMeso() {
	return meso;
    }

    public void setMeso(int meso) {
	if (meso < 0) {
	    return;
	}
        changed = true;
	this.meso = meso;
    }

    public void sendMeso(DbgAccount c) {
	c.getSession().write(MainPacketCreator.mesoStorage(slots, meso));
    }

    public boolean isFull() {
	return items.size() >= slots;
    }

    public int getSlots() {
	return slots;
    }

    public void increaseSlots(byte gain) {
        changed = true;
	this.slots += gain;
    }

    public void setSlots(byte set) {
        changed = true;
	this.slots = set;
    }

    public void close() {
	typeItems.clear();
    }
}
